module control (
	input wire [2:0] funct3,
	input wire [6:0] funct7,
	input wire [6:0] opcode,
	output reg [1:0] npc_op,
	output reg [2:0] sext_op,
	output reg rf_we,
	output reg [1:0] rf_wsel,
	output reg alub_sel,
	output reg [3:0] alu_op,
	output wire ram_we,
	output reg [1:0] rRe,
	output wire branch
);

/*
npc_op
op = 0 for npc = PC + 4
op = 1 for npc = br ? PC+offset : PC+4
op = 2 for npc = PC + offset
op = 3 for npc = radr + offset
*/
always @ (*) begin
	if (opcode[6] == 1) begin
		if (opcode[3:2] == 2'b00) npc_op = 2'b01;
		else if (opcode[3:2] == 2'b11) npc_op = 2'b10;
		else npc_op = 2'b11;
	end
	else npc_op = 2'b00;
end

/*
sext_op
op = 0 for IROM.inst[31:20]
op = 1 for IROM.inst[31:12]
op = 2 for IROM.inst[25:20]
op = 3 for IROM.inst[31:25|11:7]
op = 4 for IROM.inst[31|7|30:25|11:8]
op = 5 for IROM.inst[31|19:12|20|30:21]
*/
always @ (*) begin
	if (opcode[6:5] == 2'b00) begin
		if ((funct3 == 3'b001) | (funct3 == 3'b101)) sext_op = 3'b010;
		else sext_op = 3'b000;
	end
	else if (opcode[6:5] == 2'b01) begin
		if (opcode[4] == 1'b0) sext_op = 3'b011;
		else sext_op = 3'b001;
	end
	else begin
		if (opcode[3:2] == 2'b00) sext_op = 3'b100;
		else if (opcode[3:2] == 2'b11) sext_op = 3'b101;
		else sext_op = 3'b000;
	end
end

/*
rf_we
*/
always @ (*) begin
	if ((opcode == 7'b1100011) | (opcode == 7'b0100011)) rf_we = 1'b0;
	else rf_we = 1'b1;
end

/*
rf_wsel
rf_wsel = 0 for wD = PC + 4
rf_wsel = 1 for wD = ext
rf_wsel = 2 for wD = ALU_C
rf_wsel = 3 for wD = rdo
*/
always @ (*) begin
	if (opcode == 7'b0110111) rf_wsel = 2'b01;
	else if (opcode == 7'b0000011) rf_wsel = 2'b11;
	else if ((opcode == 7'b1101111) | (opcode == 7'b1100111)) rf_wsel = 2'b00;
	else rf_wsel = 2'b10;
end

/*
alub_sel
0 for rD2
1 for ext
*/
always @ (*) begin
	if ((opcode[6:4] == 3'b011) | opcode == 7'b1100011) alub_sel = 1'b0;
	else alub_sel = 1'b1;
end

/*
alu_op
parameter ADD = 4'd0;
parameter SUB = 4'd1;
parameter AND = 4'd2;
parameter OR  = 4'd3;
parameter XOR = 4'd4;
parameter SLL = 4'd5;
parameter SRL = 4'd6;
parameter SRA = 4'd7;
parameter BEQ = 4'd8;
parameter BNE = 4'd9;
parameter BLT = 4'd10;
parameter BGE = 4'd11;
parameter JAL = 4'd12;
*/
always @ (*) begin
	if (opcode == 7'b0000011) alu_op = 4'd0; // lw
	else if (opcode == 7'b0010011) begin
		case (funct3)
			3'b000: alu_op = 4'd0; // addi
			3'b001: alu_op = 4'd5; // slli
			3'b100: alu_op = 4'd4; // xori
			3'b101: begin
						if (funct7[5] == 1'b0) alu_op = 4'd6; // srli
						else alu_op = 4'd7; // srai
					end
			3'b110: alu_op = 4'd3; // ori
			3'b111: alu_op = 4'd2; // andi
			default: alu_op = 4'd0;
		endcase
	end
	else if (opcode == 7'b0100011) alu_op = 4'd0; // sw
	else if (opcode == 7'b0110011) begin
		case (funct3)
			3'b000: begin
						if (funct7[5] == 1'b0) alu_op = 4'd0; // add
						else alu_op = 4'd1; // sub
					end
			3'b001: alu_op = 4'd5; // sll
			3'b100: alu_op = 4'd4; // xor
			3'b101: begin
						if (funct7[5] == 1'b0) alu_op = 4'd6; // srl
						else alu_op = 4'd7; // sra
					end
			3'b110: alu_op = 4'd3; // or
			3'b111: alu_op = 4'd2; // and
			default: alu_op = 4'd0;
		endcase
	end
	else if (opcode == 7'b1100011) begin
		case (funct3)
			3'b000: alu_op = 4'd8; // beq
			3'b001: alu_op = 4'd9; // bne
			3'b100: alu_op = 4'd10; // blt
			3'b101: alu_op = 4'd11; // bge
			default: alu_op = 4'd0;
		endcase
	end
	else if (opcode == 7'b1100111 | opcode == 7'b1101111) alu_op = 4'd12; // jal & jalr
	else alu_op = 4'd0; // lui
end

/*
ram_we
*/
assign ram_we = (opcode == 7'b0100011);

/*
rRe
jal, lui: 00
add, sub, and, or, xor, sll, srl, sra, sw, beq, bne, blt, bge: 11
addi, andi, ori, xori, slli, srli, srai, lw, jalr: 10
*/
always @ (*) begin
	if (opcode == 7'b0110111 | opcode == 7'b1101111) rRe = 2'b00;
	else if (opcode == 7'b0010011 | opcode == 7'b0000011 | opcode == 7'b1100111) rRe = 2'b10;
	else rRe = 2'b11;
end

/*
branch
*/
assign branch = (opcode == 7'b1100011);

endmodule